/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.tnmk.worktime.gui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import javax.swing.JTable;
import javax.swing.SortOrder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;
import org.joda.time.DateTime;
import org.tnmk.common.util.Filter;
import org.tnmk.worktime.config.AppConfig;
import static org.tnmk.worktime.gui.MainFrame.log;
import org.tnmk.worktime.gui.history.filter.TimeRowFilter;
import org.tnmk.worktime.gui.list.ListDateRenderer;
import org.tnmk.worktime.gui.list.ListMonthRenderer;
import org.tnmk.worktime.gui.tbl.EditDeleteCellEditor;
import org.tnmk.worktime.gui.tbl.EditDeleteCellRenderer;
import org.tnmk.worktime.gui.tbl.HourDecimalCellRenderer;
import org.tnmk.worktime.gui.tbl.ImgEditDeleteCell;
import org.tnmk.worktime.gui.tbl.SessionTypeCellRenderer;
import org.tnmk.worktime.gui.tbl.TimeCellRenderer;
import org.tnmk.worktime.gui.tbl.WeekCellRenderer;
import org.tnmk.worktime.gui.utils.JComboUtils;
import org.tnmk.worktime.gui.utils.JTableUtils;
import org.tnmk.worktime.gui.widget.YesNoDialog;
import org.tnmk.worktime.service.SessionService;
import org.tnmk.worktime.service.dao.entry.SessionEntry;
import org.tnmk.worktime.service.model.SessionModel;
import org.tnmk.worktime.utils.TimePeriodUtils;

/**
 *
 * @author Administrator
 */
public class HistoryController {

  public static final int COL_INTIME = 0;
  public static final int COL_OUTTIME = 1;
  public static final int COL_WORKHOURS = 2;
  public static final int COL_ID = 3;
  private static final int NUM_WEEKS_SELECT_LIST = 10;
  private static final int NUM_MONTHS_SELECT_LIST = 12;

  private final HistoryFilterController historyFilterController;
  private final MainFrame frame;
  private SessionService service;//no setter

  public HistoryController(MainFrame frame) {
    this.frame = frame;
    this.historyFilterController = new HistoryFilterController(frame);
  }

  public void initGUI(SessionService service) {
    this.initGUIHistoryTable();
    this.initGUIFilter(service);
  }

  void initHistoryData(SessionService service) {
    this.service = service;
    this.service.loadHistory();
  }

  private void initGUIFilter(SessionService service) {
    DateTime now = service.getCurrentTime();
    DateTime endTimeFilter = TimePeriodUtils.withTimeAtEndOfDay(now);
    String endTimeFilterString = AppConfig.toString(endTimeFilter);
    frame.jtxtEndDateFilter.setText(endTimeFilterString);

    //Init combobox
    frame.jcboWeeks.setRenderer(new ListDateRenderer());
    List<DateTime> lastestWeekBeginTimes = TimePeriodUtils.getLastestWeekBeginTimesFrom(now, NUM_WEEKS_SELECT_LIST);
    JComboUtils.setListModel(frame.jcboWeeks, lastestWeekBeginTimes);

    frame.jcboMonths.setRenderer(new ListMonthRenderer());
    List<DateTime> lastestMonthBeginTimes = TimePeriodUtils.getLastestMonthBeginTimesFrom(now, NUM_MONTHS_SELECT_LIST);
    JComboUtils.setListModel(frame.jcboMonths, lastestMonthBeginTimes);

    //Default selection
    selectGUIWeekFilter();
  }

  /**
   * Only initiate GUI, not data inside.
   */
  private void initGUIHistoryTable() {
    final JTable jtbl = frame.getJtblHistory();
    //GUI
    jtbl.setAutoCreateRowSorter(true);
    jtbl.setRowHeight(20);
    jtbl.setShowGrid(false);
    jtbl.setIntercellSpacing(new Dimension(0, 0));
    jtbl.setDefaultEditor(Object.class, null);

    TableColumnModel colModel = jtbl.getColumnModel();
    WeekCellRenderer baseRenderer = new WeekCellRenderer();
    TimeCellRenderer timeRenderer = new TimeCellRenderer(baseRenderer);
    colModel.getColumn(COL_INTIME).setCellRenderer(timeRenderer);
    colModel.getColumn(COL_OUTTIME).setCellRenderer(timeRenderer);
    colModel.getColumn(COL_WORKHOURS).setCellRenderer(new HourDecimalCellRenderer(baseRenderer));
    JTableUtils.addRenderer(jtbl, COL_ID, new SessionTypeCellRenderer(frame));
    JTableUtils.addRenderer(jtbl, COL_INTIME, new SessionTypeCellRenderer(frame));
    JTableUtils.addRenderer(jtbl, COL_OUTTIME, new SessionTypeCellRenderer(frame));
    JTableUtils.addRenderer(jtbl, COL_WORKHOURS, new SessionTypeCellRenderer(frame));

    initTableWithImgEditDeleteCellEditor();

    TableColumn editCol = colModel.getColumn(COL_ID);
    editCol.setMaxWidth(40);
    editCol.setPreferredWidth(40);

    //SORT SETUP
    JTableUtils.sortColumn(jtbl, COL_INTIME, SortOrder.DESCENDING);
    JTableUtils.sortColumn(jtbl, COL_ID, false);
  }

  /**
   * @deprecated not used
   */
  @Deprecated
  private void initTableWithEditDeleteCellEditor() {
    final JTable jtbl = frame.jtblHistory;
    TableColumnModel colModel = jtbl.getColumnModel();

    EditDeleteCellEditor cellEditor = new EditDeleteCellEditor();
    cellEditor.addEditListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        editSelectedSession(jtbl);
      }
    });
    cellEditor.addDeleteListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        //Must adding below condition to avoid ArrayIndexOutOfBoundException when deleting a row
        if (jtbl.isEditing()) {
          jtbl.getCellEditor().stopCellEditing();
        }

        final int slcRowIndex = jtbl.getSelectedRow();
        final YesNoDialog deleteRowDialog = new YesNoDialog(frame, "Are you sure to delete row " + slcRowIndex + "?");
        deleteRowDialog.addYesActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent evt) {
            deleteSelectedSession(jtbl);
          }
        });
        deleteRowDialog.show();
      }
    });
    TableColumn editCol = colModel.getColumn(COL_ID);
    editCol.setCellRenderer(new EditDeleteCellRenderer());
    editCol.setCellEditor(cellEditor);
  }

  private void initTableWithImgEditDeleteCellEditor() {
    final JTable jtbl = frame.jtblHistory;;
    TableColumnModel colModel = jtbl.getColumnModel();

    ImgEditDeleteCell cellEditor = new ImgEditDeleteCell();
    cellEditor.addEditListener(new MouseAdapter() {
      //Don't use mousePressed(), it will cause error when call jtbl.getValueAt(...)
      @Override
      public void mouseReleased(MouseEvent e) {
        editSelectedSession(jtbl);
      }
    });
    cellEditor.addDeleteListener(new MouseAdapter() {
      @Override
      public void mouseReleased(MouseEvent e) {
        //Must adding this line to avoid ArrayIndexOutOfBoundException when deleting a row (may happend when changing model of table)
        if (jtbl.isEditing()) {
          jtbl.getCellEditor().stopCellEditing();
        }
        final int slcRowIndex = jtbl.getSelectedRow();
        final YesNoDialog deleteRowConfirmDialog = new YesNoDialog(frame, "Are you sure to delete row " + slcRowIndex + "?");
        deleteRowConfirmDialog.addYesActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent evt) {
            deleteSelectedSession(jtbl);
          }
        });
        deleteRowConfirmDialog.show();
      }
    });
    TableColumn editCol = colModel.getColumn(COL_ID);
    editCol.setCellRenderer(cellEditor);
    editCol.setCellEditor(cellEditor);
  }

  public static Object[] toTableRow(SessionEntry entry) {
    Object[] row = {entry.getInTime(), entry.getOutTime(), entry.getWorkedHours(), entry.getId()};
    return row;
  }

  public static SessionEntry toTableEntry(Object[] row) {
    SessionEntry entry = new SessionEntry();
    entry.setId((Integer) row[COL_ID]);
    entry.setInTime((DateTime) row[COL_INTIME]);
    entry.setOutTime((DateTime) row[COL_OUTTIME]);
    entry.setWorkedHours((float) row[COL_WORKHOURS]);
    return entry;
  }

  void toHistoryGUI() {
    DefaultTableModel tblModel = (DefaultTableModel)frame.jtblHistory.getModel();
    tblModel.setRowCount(0);//clear all rows
    List<SessionEntry> history = service.loadHistory();
    for (SessionEntry wtModel : history) {
      Object[] row = toTableRow(wtModel);
      tblModel.addRow(row);
    }
  }

  private void editSelectedSession(JTable jtbl) {
    //Save the current session to Service first.
    service.addOrUpdateSessionAndSpends(this.frame.detailController.getViewModel());

    //Load other session from Service
    int slcRowIndex = jtbl.getSelectedRow();
    Object slcIdVal = jtbl.getValueAt(slcRowIndex, COL_ID);//getValueAt(slcRowIndex, COL_ID);
    Integer slcId = (Integer) slcIdVal;
    SessionEntry session = service.findSession(slcId);
    DetailController detailController = frame.getDetailController();
    SessionModel viewModel = new SessionModel(session);
    service.recalculateModel(viewModel);
    detailController.setViewModel(viewModel);
    detailController.toDetailGUI();
  }

  private void deleteSelectedSession(JTable jtbl) {
    int slcRowIndex = jtbl.getSelectedRow();
    Object slcIdVal = jtbl.getValueAt(slcRowIndex, COL_ID);//getValueAt(slcRowIndex, COL_ID);
    log.trace("Delete Row " + slcRowIndex + ", slcIdVal:" + slcIdVal.getClass() + "#" + slcIdVal);
    //If deleting session == currentNewSession: don't delete it.
    if (slcIdVal.equals(service.getCurrentSession().getId())) {
      log.debug("Don't delete the current Session");
      return;
    }

    //Remove selectedId
    Integer slcId = (Integer) slcIdVal;
    service.deleteSessionAndRelatedSpends(slcId);
    JTableUtils.removeRow(jtbl, slcRowIndex, COL_ID);

    //If deleting session == current editing Session. Should select other session after deleting
    if (slcIdVal.equals(frame.detailController.getViewModel().getId())) {
      SessionModel currentSession = service.getCurrentSession();
      frame.detailController.setViewModel(currentSession);
      frame.detailController.toDetailGUI();
    }
  }

  public Filter<DateTime> getSelectedDateTimeFilter() {
    TableRowSorter sorter = (TableRowSorter)this.frame.jtblHistory.getRowSorter();
    TimeRowFilter rowFilter = (TimeRowFilter) sorter.getRowFilter();
    return rowFilter.getFilter();
  }

  public List<SessionEntry> loadFilteredSessions() {
    Filter<DateTime> filter = getSelectedDateTimeFilter();
    return service.loadHistoryWithInTimeFilter(filter);
  }

  public void selectGUIWeekFilter() {
    if (frame.jradWeekFilter.isSelected()) {
      historyFilterController.filterWeekHistory();
      spendAnalysisWhenNeccessary();
    }
  }

  public void selectGUIMonthFilter() {
    if (frame.jradMonthFilter.isSelected()) {
      historyFilterController.filterMonthHistory();
      spendAnalysisWhenNeccessary();
    }
  }

  public void selectGUIRangeFilter() {
    if (frame.jradRangeFilter.isSelected()) {
      historyFilterController.filterRangeHistory(frame.jtxtBeginDateFilter.getText(), frame.jtxtEndDateFilter.getText(), AppConfig.TIME_PATTERN);
      spendAnalysisWhenNeccessary();
    }
  }

  private void spendAnalysisWhenNeccessary() {
    if (frame.jpanSpendAnalysis.equals(frame.jcompDetails.getSelectedComponent())) {
      frame.spendAnalysisController.filterSpendInCurrentFilteredSessions();
    }
  }
}
